using System;
using System.Collections.Generic;
using System.Threading;

namespace BridgeUI
{
    internal class SemaphoreSlim
    {
        public SemaphoreSlim(int initSemaphore)
        {
            semaphore = initSemaphore;
        }

        private object __evt = new object();
        AutoResetEvent evt
        {
            get
            {
                if (!(__evt is AutoResetEvent))
                {
                    lock (__evt)
                    {
                        if (!(__evt is AutoResetEvent))
                            __evt = new AutoResetEvent(semaphore > 0);
                    }
                }
                return __evt as AutoResetEvent;
            }
        }
        private const int SpinTime = 100, SleepTime = 1;
        private int semaphore, waiting;
        public int Semaphore { get { return semaphore; }}
        public  int Waiting { get { return waiting; } }

        bool Spin()
        {
            for (int i = 0; i < SpinTime; ++i)
            {
                if (Interlocked.Decrement(ref semaphore) >= 0) return true;
                Interlocked.Increment(ref semaphore);
                Thread.Sleep(SleepTime);
            }
            return false;
        }
        public void Wait()
        {
            if (Spin()) return;
            Interlocked.Increment(ref waiting);
            if (Interlocked.Decrement(ref semaphore) < 0)
                evt.WaitOne();
            Interlocked.Decrement(ref waiting);
            if (Interlocked.Decrement(ref semaphore) > 0 && waiting > 0) evt.Set();
        }

        public void Release()
        {
            if (Interlocked.Increment(ref semaphore) == 1 && waiting > 0) evt.Set();
        }
    }

    internal class ResourceLock
    {

        private readonly SemaphoreSlim reader = new SemaphoreSlim(1), writer = new SemaphoreSlim(1);
        private int readerCount = 0;

        public int ReaderCount { get { return readerCount; } }
        public bool Reading { get { return readerCount != 0; } }
        public  bool Writing { get; private set; }

        public void ReaderWait()
        {
            reader.Wait();
            if (Interlocked.Increment(ref readerCount) == 1)
                writer.Wait();
            reader.Release();
        }

        public void WriterWait()
        {
            reader.Wait();
            writer.Wait();
        }

        public void ReaderRelease()
        {
            if (Interlocked.Decrement(ref readerCount) == 0)
                writer.Release();
        }

        public void WriterRelease()
        {
            writer.Release();
            reader.Release();
        }

        class WriteLockRange : IDisposable
        {
            public WriteLockRange(ResourceLock locker)
            {
                Locker = locker;
                locker.WriterWait();
            }

            private ResourceLock Locker;

            public void Dispose()
            {
                Locker.WriterRelease();
            }
        }
        class ReadLockRange : IDisposable
        {
            public ReadLockRange(ResourceLock locker)
            {
                Locker = locker;
                Locker.ReaderRelease();
            }

            private ResourceLock Locker;

            public void Dispose()
            {
                Locker.ReaderRelease();
            }
        }
        public IDisposable ReaderRange()
        {
            return new ReadLockRange(this);
        }
        public IDisposable WriterRange()
        {
            return new WriteLockRange(this);
        }
    }
}
